package sc.tool.audio;

import java.io.IOException;
import java.util.Stack;

import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioRecord;
import android.media.AudioTrack;
import android.media.MediaRecorder;
import android.util.Log;

import com.dreamfish.record.WaveHeader;

// 回声消除的方法（让音频播放音量小于录制的环境音量，可降低至录制音量的0.5，2次回声周期，即可恢复安静状态）
/** 
 * 对音频的录制与播放进行简化封装（以实现音频的实时录制和实时播放）
 * 
 * 用法：
 * 
 * 录制实时音频，
 * 1. AudioTool.RecordingStart(final CallBack_bytes call);	// 进行实时音频的录制，通过CallBack_bytes实时返回录制的音频数据
 * 2. AudioTool.RecodingStop();		// 停止音频的录制
 * 
 * 播放实时音频，
 * 3. AudioPlayer audioPlayer = new AudioPlayer();
 *    audioPlayer.Play(bytes);		// bytes为实时音频数据，实时不断调用，实时播放
 * 4. audioPlayer.Stop();			// 停止实时音频的播放
 * 
 * scimence 2020-05-23 18:15
 * */
public class AudioTool
{
	/** 实时：本地录制，本地播放 示例 */
	public static void Example()
	{
		// 启动音频实时录制
		AudioTool.RecordingStart(new CallBack_bytes()
		{
			AudioPlayer audioPlayer = new AudioPlayer();	// 创建AudioPlayer用于音频播放，此处仅演示本地播放
			
			@Override
			public void F(byte[] data)
			{
				audioPlayer.Play(data);	// 对实时获取的音频，进行实时播放
				
				// 1、一般是将data实时传输至服务器端，由服务器转发至远程客户端；
				// 2、在远程客户端，用audioPlayer播放实时音频。
			}
		});
	}
	
	// --------------------------
	
	/** 定义一个byte数组回调 */
	public interface CallBack_bytes
	{
		public void F(byte[] data);
	}

	// --------------------------
	// 通过AudioRecord进行音频的录制（实时输出音频数据）
	
	/** AudioRecord单例对象，用于录制本地音频 */
	private static AudioRecord audioRecoder;
	
	/** 音频是否正在录制 */
	private static boolean IsRecording = false;
	
	private static int bufferSize = 102400;		// 缓存大小
//	private static int bufferSize = 204800;		// 缓存大小
//	private static int bufferSize = 1024 * 50;	// 缓存大小，缓存越大处理的延时越长
	
	public static int RateInHz = 16000;
	
	/** 获取AudioRecord单例对象 */
	private static AudioRecord GetAudioRecord()
	{
		if(audioRecoder == null)
		{
			// 配置AudioRecord
//			int audioSource = MediaRecorder.AudioSource.MIC;	// 声音来源
			int audioSource = MediaRecorder.AudioSource.VOICE_COMMUNICATION;	// 声音来源
//			int audioSource = MediaRecorder.AudioSource.VOICE_RECOGNITION;	// 声音来源
			int sampleRate = RateInHz;								// 采样率 Hz
			int channelConfig = AudioFormat.CHANNEL_IN_MONO;	// 音频通道的配置 MONO 单声道
			int audioFormat = AudioFormat.ENCODING_PCM_16BIT;	// 返回音频数据的格式 
			int minBufferSize = AudioRecord.getMinBufferSize(sampleRate, channelConfig, audioFormat);	//AudioRecord能接受的最小的buffer大小
			
			audioRecoder = new AudioRecord(audioSource, sampleRate, channelConfig, audioFormat, Math.max(minBufferSize, 2048));
		}
		
		return audioRecoder;
	}
	
	/** 开启音频录制（将实时获取的音频数据，通过call回调返回） */
	public static void RecordingStart(final CallBack_bytes call)
	{
		if(IsRecording) return;
		IsRecording = true;
		
		
		final AudioRecord audioRecoder = GetAudioRecord();
		audioRecoder.startRecording();
		
		// 在新的线程中持续执行音频
		Thread recodingTread = new Thread(new Runnable()
		{
			@Override
			public void run()
			{
//				android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_URGENT_AUDIO);
				
				byte[] tmp = new byte[bufferSize];
				
				while(IsRecording)
				{
					int count = audioRecoder.read(tmp, 0, bufferSize);	// 在线程中实时读取，音频数据
					
//					PauseRecoding(3000);	// 暂停录制1秒钟
					
					if (count > 0)
					{
						if(call != null)
						{
//							byte[] data = new byte[count];
							
							byte[] data = PCM2WAV(tmp, count);
							if(data != null) call.F(data);	// 输出获取到的音频数据
						}
					}
				}
			}
		});
		recodingTread.start();
	}
	
//	private static boolean PauseRecodingIsRunning = false;
//	private static void PauseRecoding(long millionSecond)
//	{
//		if(!IsRecording) return;
//		if(PauseRecodingIsRunning) return;
//		PauseRecodingIsRunning = true;
//		
//		audioRecoder.stop();
//		ThreadTool.RunInMainThread(new ThreadPram()
//		{
//			@Override
//			public void Function()
//			{
//				audioRecoder.startRecording();
//				PauseRecodingIsRunning = false;
//			}
//		}, millionSecond);
//	}
//	
//	private static int releaseI = 0;
	
	
//	/** 对data数据进行精简，剔除开头和结尾的空数据0 */
//	private static byte[] Trim(byte[] data, int count)
//	{
//		int start = 0;
//		for(int i=0; i<count && i<data.length; i++)
//		{
//			if(data[i]==0) start = i;
//			else break;
//		}
//		
//		int end = count-1;
//		if(end > data.length-1) end = data.length-1;
//		for(int i=end; i>=0; i--)
//		{
//			if(data[i]==0) end = i;
//			else break;
//		}
//		
//		if(end > start)
//		{
//			byte[] tmp = new byte[end - start];
//			for(int i = 0; i<tmp.length; i++)
//			{
//				tmp[i] = data[start + 1];
//			}
//			return tmp;
//		}
//		else return null;
//	}
	
	/** 将PCM数据转化为WAV */
	private static byte[] PCM2WAV(byte[] data, int count)
	{
		int headLen = 44;
		
//		data = Trim(data, count);	// 数据精简
//		count = data.length;
		
		byte[] tmp = new byte[count + headLen];
		for(int i=0; i<count; i++) 
		{
			if(data[i] > 25 || data[i] < -25) tmp[i+40] =  0;	// 降低音频音量，以消除噪声
			else tmp[i+40] = (byte)(data[i] * 0.9);	
//			else tmp[i+40] = data[i];	
		}
		
//		for(int i=0; i<count; i++) tmp[i+40] = (byte)(data[i] * 0.75);	// 降低音频音量，以消除噪声
		
//		int max = 0, min = 0;
//		for(int j = 0; j<data.length; j++)
//		{
//			if(data[j] > max) max = data[j];
//			if(data[j] < min) min = data[j];
//			
//		}
//		
//		if(max == 0 && min == 0) 
//			return null;
//		
//		releaseI++;
//		if(releaseI > 2) releaseI = 0;
//		
//		for(int i=0; i<count; i++) 
//		{
//			if(i % 2 == releaseI) tmp[i+40] =  0;		// 交替损失一半音频数据，以降噪
//			
//			else if(data[i] > 20 || data[i] < -20) tmp[i+40] =  0;
////			else tmp[i+40] =  (byte)(data[i]*0.9) ;		// 降低音频音量，以消除噪声
//			else tmp[i+40] =  (byte)(data[i] - 3) ;		// 降低音频音量，以消除噪声
//		}
		
		int TOTAL_SIZE = count;
		// 填入参数，比特率等等。这里用的是16位单声道 8000 hz
		WaveHeader header = new WaveHeader();
		// 长度字段 = 内容的大小（TOTAL_SIZE) +
		// 头部字段的大小(不包括前面4字节的标识符RIFF以及fileLength本身的4字节)
		header.fileLength = TOTAL_SIZE + (44 - 8);
		header.FmtHdrLeth = 16;
		header.BitsPerSample = 16;
		header.Channels = 2;
		header.FormatTag = 0x0001;
//		header.SamplesPerSec = 8000;
		header.SamplesPerSec = RateInHz;
		header.BlockAlign = (short) (header.Channels * header.BitsPerSample / 8);
		header.AvgBytesPerSec = header.BlockAlign * header.SamplesPerSec;
		header.DataHdrLeth = TOTAL_SIZE;
		
		byte[] h = null;
		try
		{
			h = header.getHeader();
			
			for(int i=0; i<40; i++) tmp[i] = h[i];
		}
		catch (IOException e1)
		{
			Log.e("PcmToWav", e1.getMessage());
			return null;
		}
		
		if (h.length != 44) // WAV标准，头部应该是44字节,如果不是44个字节则不进行转换文件
			return null;
		
		return tmp;
	}
	
	/** 停止音频录制 */
	public static void RecodingStop()
	{
		if(!IsRecording) return;
		IsRecording = false;
		
		if(audioRecoder != null)
		{
			audioRecoder.stop();
			audioRecoder.release();
			audioRecoder = null;
		}
	}
	
	// --------------------------
	// 通过AudioTrack进行音频的实时播放
	
	/** AudioPlayer用于播放实时音频。
	 * 
	 * 用法：
	 * 1. AudioPlayer audioPlayer = new AudioPlayer();		// 创建新实例，以播放音频（因为有可能会有多个远程音频终端，可创建多个实例分别播放）
	 * 2. audioPlayer.Start();			// 启动实时播放逻辑
	 * 3. audioPlayer.PutData(bytes);	// bytes为实时音频数据，不断调用PutData持续输入
	 * 4. audioPlayer.Stop(); 			// 停止实时播放
	 * 
	 * scimence 2020-05-23 18:15
	 * */
	public static class AudioPlayer
	{
		private AudioTrack audioTrack;
		private boolean IsPlaying = false;	// 记录音频是否正在播放
		
		/** 缓存获取到的音频数据 */
		public Stack<byte[]> audioCache = new Stack<byte[]>();
		
		/** 停止音频录制 */
		private AudioTrack GetAudioTrack()
		{
			if(audioTrack == null)
			{
				int bufferSize = AudioTrack.getMinBufferSize(RateInHz, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT);
				audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, RateInHz, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT, bufferSize * 2, AudioTrack.MODE_STREAM);
			}
			
			return audioTrack;
		}
		
		/** 启动实时音频播放 */
		public void Start()
		{
			if(IsPlaying) return;
			IsPlaying = true;
			
			final AudioTrack audioTrack = GetAudioTrack();
			audioTrack.play();	// 启动音频播放
			
			// 在新的线程中持续执行音频
			Thread playTread = new Thread(new Runnable()
			{
				@Override
				public void run()
				{
					android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_URGENT_AUDIO);
					
					audioTrack.play();
					while (IsPlaying)
					{
						if(!audioCache.isEmpty())
						{
//							audioTrack.play();	// 启动音频播放
							byte[] data = AudioPlayer.this.audioCache.pop();	// 获取音频数据
							audioTrack.write(data, 0, data.length);				// 将数据写入audioTrack，进行实时播放
						}
					}
					
				}
			});
			
			playTread.start();
		}
		
		/** 
		 * 实时写入，待播放的音频数据
		 * （Start()调用一次后，会自动获取音频数据，依次进行播放；
		 * 若Start函数未调用，则会一直缓存音频数据） 
		 **/
		public void PutData(final byte[] audioData)
		{
			if(audioData == null || audioData.length == 0) return;
			audioCache.push(audioData);
			
//			// 在主线程中，对帧图像进行处理，不阻塞当前线程（否则会丢帧、帧视屏会有卡顿的情况）
//			ThreadTool.RunInSingleThread("PutData", new ThreadPram()
//			{
//				@Override
//				public void Function()
//				{
//					audioTrack.write(audioData, 0, audioData.length); // 将数据写入audioTrack，进行实时播放
//				}
//			});
		}
		
		/** 停止播放音频 */
		public void Stop()
		{
			if(!IsPlaying) return;
			IsPlaying = false;
			
			audioTrack.stop();
		}
		
		//---------
		
		/** 播放音频数据（会将audioData放入音频数据缓存中，并自动启动播放） */
		public void Play(byte[] audioData)
		{
			// 对以下两个函数接口，进行封装，简化调用
			Start();
			PutData(audioData);
		}
	}
	
}

